//
// Copyright (c) 2010 Serge A. Zaitsev
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// Downloaded from https://github.com/zserge/jsmn

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#define JSMN_STRICT 1

/**
 * JSON type identifier. Basic types are:
 * 	o Object
 * 	o Array
 * 	o String
 * 	o Other primitive: number, boolean (true/false) or null
 */
typedef enum {
    JSMN_UNDEFINED = 0,
    JSMN_OBJECT = 1,
    JSMN_ARRAY = 2,
    JSMN_STRING = 3,
    JSMN_PRIMITIVE = 4
} jsmntype_t;

enum jsmnerr {
    /* Not enough tokens were provided */
    JSMN_ERROR_NOMEM = -1,
    /* Invalid character inside JSON string */
    JSMN_ERROR_INVAL = -2,
    /* The string is not a full JSON packet, more bytes expected */
    JSMN_ERROR_PART = -3
};

/**
 * JSON token description.
 * @param		type	type (object, array, string etc.)
 * @param		start	start position in JSON data string
 * @param		end		end position in JSON data string
 */
typedef struct {
    jsmntype_t type;
    int start;
    int end;
    int size;
#ifdef JSMN_PARENT_LINKS
    int parent;
#endif
} jsmntok_t;

/**
 * JSON parser. Contains an array of token blocks available. Also stores
 * the string being parsed now and current position in that string
 */
typedef struct {
    unsigned int pos; /* offset in the JSON string */
    unsigned int toknext; /* next token to allocate */
    int toksuper; /* superior token node, e.g parent object or array */
} jsmn_parser;

/**
 * Create JSON parser over an array of tokens
 */
void jsmn_init(jsmn_parser *parser);

/**
 * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
 * a single JSON object.
 */
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
               jsmntok_t *tokens, unsigned int num_tokens);

/**
 * Allocates a fresh unused token from the token pull.
 */
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
                                   jsmntok_t *tokens, size_t num_tokens) {
    jsmntok_t *tok;
    if (parser->toknext >= num_tokens) {
        return NULL;
    }
    tok = &tokens[parser->toknext++];
    tok->start = tok->end = -1;
    tok->size = 0;
#ifdef JSMN_PARENT_LINKS
    tok->parent = -1;
#endif
    return tok;
}

/**
 * Fills token type and boundaries.
 */
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
                            int start, int end) {
    token->type = type;
    token->start = start;
    token->end = end;
    token->size = 0;
}

/**
 * Fills next available token with JSON primitive.
 */
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
                                size_t len, jsmntok_t *tokens, size_t num_tokens) {
    jsmntok_t *token;
    int start;

    start = parser->pos;

    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
        switch (js[parser->pos]) {
#ifndef JSMN_STRICT
            /* In strict mode primitive must be followed by "," or "}" or "]" */
            case ':':
#endif
            case '\t' :
            case '\r' :
            case '\n' :
            case ' ' :
            case ','  :
            case ']'  :
            case '}' :
                goto found;
        }
        if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
            parser->pos = start;
            return JSMN_ERROR_INVAL;
        }
    }
#ifdef JSMN_STRICT
    /* In strict mode primitive must be followed by a comma/object/array */
    parser->pos = start;
    return JSMN_ERROR_PART;
#endif

found:
    if (tokens == NULL) {
        parser->pos--;
        return 0;
    }
    token = jsmn_alloc_token(parser, tokens, num_tokens);
    if (token == NULL) {
        parser->pos = start;
        return JSMN_ERROR_NOMEM;
    }
    jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
    token->parent = parser->toksuper;
#endif
    parser->pos--;
    return 0;
}

/**
 * Fills next token with JSON string.
 */
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
                             size_t len, jsmntok_t *tokens, size_t num_tokens) {
    jsmntok_t *token;

    int start = parser->pos;

    parser->pos++;

    /* Skip starting quote */
    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
        char c = js[parser->pos];

        /* Quote: end of string */
        if (c == '\"') {
            if (tokens == NULL) {
                return 0;
            }
            token = jsmn_alloc_token(parser, tokens, num_tokens);
            if (token == NULL) {
                parser->pos = start;
                return JSMN_ERROR_NOMEM;
            }
            jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
#ifdef JSMN_PARENT_LINKS
            token->parent = parser->toksuper;
#endif
            return 0;
        }

        /* Backslash: Quoted symbol expected */
        if (c == '\\' && parser->pos + 1 < len) {
            int i;
            parser->pos++;
            switch (js[parser->pos]) {
                /* Allowed escaped symbols */
                case '\"':
                case '/' :
                case '\\' :
                case 'b' :
                case 'f' :
                case 'r' :
                case 'n'  :
                case 't' :
                    break;
                /* Allows escaped symbol \uXXXX */
                case 'u':
                    parser->pos++;
                    for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
                        /* If it isn't a hex character we have an error */
                        if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
                                (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
                                (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
                            parser->pos = start;
                            return JSMN_ERROR_INVAL;
                        }
                        parser->pos++;
                    }
                    parser->pos--;
                    break;
                /* Unexpected symbol */
                default:
                    parser->pos = start;
                    return JSMN_ERROR_INVAL;
            }
        }
    }
    parser->pos = start;
    return JSMN_ERROR_PART;
}

/**
 * Parse JSON string and fill tokens.
 */
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
               jsmntok_t *tokens, unsigned int num_tokens) {
    int r;
    int i;
    jsmntok_t *token;
    int count = parser->toknext;

    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
        char c;
        jsmntype_t type;

        c = js[parser->pos];
        switch (c) {
            case '{':
            case '[':
                count++;
                if (tokens == NULL) {
                    break;
                }
                token = jsmn_alloc_token(parser, tokens, num_tokens);
                if (token == NULL)
                    return JSMN_ERROR_NOMEM;
                if (parser->toksuper != -1) {
                    tokens[parser->toksuper].size++;
#ifdef JSMN_PARENT_LINKS
                    token->parent = parser->toksuper;
#endif
                }
                token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
                token->start = parser->pos;
                parser->toksuper = parser->toknext - 1;
                break;
            case '}':
            case ']':
                if (tokens == NULL)
                    break;
                type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
#ifdef JSMN_PARENT_LINKS
                if (parser->toknext < 1) {
                    return JSMN_ERROR_INVAL;
                }
                token = &tokens[parser->toknext - 1];
                for (;;) {
                    if (token->start != -1 && token->end == -1) {
                        if (token->type != type) {
                            return JSMN_ERROR_INVAL;
                        }
                        token->end = parser->pos + 1;
                        parser->toksuper = token->parent;
                        break;
                    }
                    if (token->parent == -1) {
                        break;
                    }
                    token = &tokens[token->parent];
                }
#else
                for (i = parser->toknext - 1; i >= 0; i--) {
                    token = &tokens[i];
                    if (token->start != -1 && token->end == -1) {
                        if (token->type != type) {
                            return JSMN_ERROR_INVAL;
                        }
                        parser->toksuper = -1;
                        token->end = parser->pos + 1;
                        break;
                    }
                }
                /* Error if unmatched closing bracket */
                if (i == -1) return JSMN_ERROR_INVAL;
                for (; i >= 0; i--) {
                    token = &tokens[i];
                    if (token->start != -1 && token->end == -1) {
                        parser->toksuper = i;
                        break;
                    }
                }
#endif
                break;
            case '\"':
                r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
                if (r < 0) return r;
                count++;
                if (parser->toksuper != -1 && tokens != NULL)
                    tokens[parser->toksuper].size++;
                break;
            case '\t' :
            case '\r' :
            case '\n' :
            case ' ':
                break;
            case ':':
                parser->toksuper = parser->toknext - 1;
                break;
            case ',':
                if (tokens != NULL && parser->toksuper != -1 &&
                        tokens[parser->toksuper].type != JSMN_ARRAY &&
                        tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
                    parser->toksuper = tokens[parser->toksuper].parent;
#else
                    for (i = parser->toknext - 1; i >= 0; i--) {
                        if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
                            if (tokens[i].start != -1 && tokens[i].end == -1) {
                                parser->toksuper = i;
                                break;
                            }
                        }
                    }
#endif
                }
                break;
#ifdef JSMN_STRICT
            /* In strict mode primitives are: numbers and booleans */
            case '-':
            case '0':
            case '1' :
            case '2':
            case '3' :
            case '4':
            case '5':
            case '6':
            case '7' :
            case '8':
            case '9':
            case 't':
            case 'f':
            case 'n' :
                /* And they must not be keys of the object */
                if (tokens != NULL && parser->toksuper != -1) {
                    jsmntok_t *t = &tokens[parser->toksuper];
                    if (t->type == JSMN_OBJECT ||
                            (t->type == JSMN_STRING && t->size != 0)) {
                        return JSMN_ERROR_INVAL;
                    }
                }
#else
            /* In non-strict mode every unquoted value is a primitive */
            default:
#endif
                r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
                if (r < 0) return r;
                count++;
                if (parser->toksuper != -1 && tokens != NULL)
                    tokens[parser->toksuper].size++;
                break;

#ifdef JSMN_STRICT
            /* Unexpected char in strict mode */
            default:
                return JSMN_ERROR_INVAL;
#endif
        }
    }

    if (tokens != NULL) {
        for (i = parser->toknext - 1; i >= 0; i--) {
            /* Unmatched opened object or array */
            if (tokens[i].start != -1 && tokens[i].end == -1) {
                return JSMN_ERROR_PART;
            }
        }
    }

    return count;
}

/**
 * Creates a new parser based over a given  buffer with an array of tokens
 * available.
 */
void jsmn_init(jsmn_parser *parser) {
    parser->pos = 0;
    parser->toknext = 0;
    parser->toksuper = -1;
}


static int vtokeq(const char *s, jsmntok_t *t, int numtok, va_list ap) {
    if (numtok > 0) {
        int i, start, end, size;
        int type;
        char *value;

        size = -1;
        value = NULL;
        for (i = 0; i < numtok; i++) {
            type = va_arg(ap, int);
            if (type == JSMN_STRING) {
                value = va_arg(ap, char *);
                size = va_arg(ap, int);
                start = end = -1;
            } else if (type == JSMN_PRIMITIVE) {
                value = va_arg(ap, char *);
                start = end = size = -1;
            } else {
                start = va_arg(ap, int);
                end = va_arg(ap, int);
                size = va_arg(ap, int);
                value = NULL;
            }
            if (t[i].type != type) {
                printf("token %d type is %d, not %d\n", i, t[i].type, type);
                return 0;
            }
            if (start != -1 && end != -1) {
                if (t[i].start != start) {
                    printf("token %d start is %d, not %d\n", i, t[i].start, start);
                    return 0;
                }
                if (t[i].end != end ) {
                    printf("token %d end is %d, not %d\n", i, t[i].end, end);
                    return 0;
                }
            }
            if (size != -1 && t[i].size != size) {
                printf("token %d size is %d, not %d\n", i, t[i].size, size);
                return 0;
            }

            if (s != NULL && value != NULL) {
                const char *p = s + t[i].start;
                if (strlen(value) != t[i].end - t[i].start ||
                        strncmp(p, value, t[i].end - t[i].start) != 0) {
                    printf("token %d value is %.*s, not %s\n", i, t[i].end-t[i].start,
                           s+t[i].start, value);
                    return 0;
                }
            }
        }
    }
    return 1;
}

static int tokeq(const char *s, jsmntok_t *tokens, int numtok, ...) {
    int ok;
    va_list args;
    va_start(args, numtok);
    ok = vtokeq(s, tokens, numtok, args);
    va_end(args);
    return ok;
}

static int parse(const char *s, int status, int numtok, ...) {
    int r;
    int ok = 1;
    va_list args;
    jsmn_parser p;
    jsmntok_t *t = malloc(numtok * sizeof(jsmntok_t));

    jsmn_init(&p);
    r = jsmn_parse(&p, s, strlen(s), t, numtok);
    if (r != status) {
        printf("status is %d, not %d\n", r, status);
        return 0;
    }

    if (status >= 0) {
        va_start(args, numtok);
        ok = vtokeq(s, t, numtok, args);
        va_end(args);
    }
    free(t);
    return ok;
}

static int test_passed = 0;
static int test_failed = 0;

/* Terminate current test with error */
#define fail()	return __LINE__

/* Successfull end of the test case */
#define done() return 0

/* Check single condition */
#define check(cond) do { if (!(cond)) fail(); } while (0)

/* Test runner */
static void test(int (*func)(void), const char *name) {
    int r = func();
    if (r == 0) {
        test_passed++;
    } else {
        test_failed++;
        printf("FAILED: %s (at line %d)\n", name, r);
    }
}


int test_empty(void) {
    check(parse("{}", 1, 1,
                JSMN_OBJECT, 0, 2, 0));
    check(parse("[]", 1, 1,
                JSMN_ARRAY, 0, 2, 0));
    check(parse("[{},{}]", 3, 3,
                JSMN_ARRAY, 0, 7, 2,
                JSMN_OBJECT, 1, 3, 0,
                JSMN_OBJECT, 4, 6, 0));
    return 0;
}

int test_object(void) {
    check(parse("{\"a\":0}", 3, 3,
                JSMN_OBJECT, 0, 7, 1,
                JSMN_STRING, "a", 1,
                JSMN_PRIMITIVE, "0"));
    check(parse("{\"a\":[]}", 3, 3,
                JSMN_OBJECT, 0, 8, 1,
                JSMN_STRING, "a", 1,
                JSMN_ARRAY, 5, 7, 0));
    check(parse("{\"a\":{},\"b\":{}}", 5, 5,
                JSMN_OBJECT, -1, -1, 2,
                JSMN_STRING, "a", 1,
                JSMN_OBJECT, -1, -1, 0,
                JSMN_STRING, "b", 1,
                JSMN_OBJECT, -1, -1, 0));
    check(parse("{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }", 7, 7,
                JSMN_OBJECT, -1, -1, 3,
                JSMN_STRING, "Day", 1,
                JSMN_PRIMITIVE, "26",
                JSMN_STRING, "Month", 1,
                JSMN_PRIMITIVE, "9",
                JSMN_STRING, "Year", 1,
                JSMN_PRIMITIVE, "12"));
    check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5,
                JSMN_OBJECT, -1, -1, 2,
                JSMN_STRING, "a", 1,
                JSMN_PRIMITIVE, "0",
                JSMN_STRING, "b", 1,
                JSMN_STRING, "c", 0));

#ifdef JSMN_STRICT
    check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3));
    check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3));
    check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3));
    check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3));
    check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5));
    /* FIXME */
    /*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/
    /*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/
    /*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/
    /*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/
    /*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/
    /*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/
#endif
    return 0;
}

int test_array(void) {
    /* FIXME */
    /*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/
    /*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/
    check(parse("[10]", 2, 2,
                JSMN_ARRAY, -1, -1, 1,
                JSMN_PRIMITIVE, "10"));
    check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3));
    /* FIXME */
    /*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/
    return 0;
}

int test_primitive(void) {
    check(parse("{\"boolVar\" : true }", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "boolVar", 1,
                JSMN_PRIMITIVE, "true"));
    check(parse("{\"boolVar\" : false }", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "boolVar", 1,
                JSMN_PRIMITIVE, "false"));
    check(parse("{\"nullVar\" : null }", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "nullVar", 1,
                JSMN_PRIMITIVE, "null"));
    check(parse("{\"intVar\" : 12}", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "intVar", 1,
                JSMN_PRIMITIVE, "12"));
    check(parse("{\"floatVar\" : 12.345}", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "floatVar", 1,
                JSMN_PRIMITIVE, "12.345"));
    return 0;
}

int test_string(void) {
    check(parse("{\"strVar\" : \"hello world\"}", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "strVar", 1,
                JSMN_STRING, "hello world", 0));
    check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "strVar", 1,
                JSMN_STRING, "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0));
    check(parse("{\"strVar\": \"\"}", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "strVar", 1,
                JSMN_STRING, "", 0));
    check(parse("{\"a\":\"\\uAbcD\"}", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "a", 1,
                JSMN_STRING, "\\uAbcD", 0));
    check(parse("{\"a\":\"str\\u0000\"}", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "a", 1,
                JSMN_STRING, "str\\u0000", 0));
    check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "a", 1,
                JSMN_STRING, "\\uFFFFstr", 0));
    check(parse("{\"a\":[\"\\u0280\"]}", 4, 4,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "a", 1,
                JSMN_ARRAY, -1, -1, 1,
                JSMN_STRING, "\\u0280", 0));

    check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3));
    check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3));
    check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4));
    return 0;
}

int test_partial_string(void) {
    int i;
    int r;
    jsmn_parser p;
    jsmntok_t tok[5];
    const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}";

    jsmn_init(&p);
    for (i = 1; i <= strlen(js); i++) {
        r = jsmn_parse(&p, js, i, tok, sizeof(tok)/sizeof(tok[0]));
        if (i == strlen(js)) {
            check(r == 5);
            check(tokeq(js, tok, 5,
                        JSMN_OBJECT, -1, -1, 2,
                        JSMN_STRING, "x", 1,
                        JSMN_STRING, "va\\\\ue", 0,
                        JSMN_STRING, "y", 1,
                        JSMN_STRING, "value y", 0));
        } else {
            check(r == JSMN_ERROR_PART);
        }
    }
    return 0;
}

int test_partial_array(void) {
#ifdef JSMN_STRICT
    int r;
    int i;
    jsmn_parser p;
    jsmntok_t tok[10];
    const char *js = "[ 1, true, [123, \"hello\"]]";

    jsmn_init(&p);
    for (i = 1; i <= strlen(js); i++) {
        r = jsmn_parse(&p, js, i, tok, sizeof(tok)/sizeof(tok[0]));
        if (i == strlen(js)) {
            check(r == 6);
            check(tokeq(js, tok, 6,
                        JSMN_ARRAY, -1, -1, 3,
                        JSMN_PRIMITIVE, "1",
                        JSMN_PRIMITIVE, "true",
                        JSMN_ARRAY, -1, -1, 2,
                        JSMN_PRIMITIVE, "123",
                        JSMN_STRING, "hello", 0));
        } else {
            check(r == JSMN_ERROR_PART);
        }
    }
#endif
    return 0;
}

int test_array_nomem(void) {
    int i;
    int r;
    jsmn_parser p;
    jsmntok_t toksmall[10], toklarge[10];
    const char *js;

    js = "  [ 1, true, [123, \"hello\"]]";

    for (i = 0; i < 6; i++) {
        jsmn_init(&p);
        memset(toksmall, 0, sizeof(toksmall));
        memset(toklarge, 0, sizeof(toklarge));
        r = jsmn_parse(&p, js, strlen(js), toksmall, i);
        check(r == JSMN_ERROR_NOMEM);

        memcpy(toklarge, toksmall, sizeof(toksmall));

        r = jsmn_parse(&p, js, strlen(js), toklarge, 10);
        check(r >= 0);
        check(tokeq(js, toklarge, 4,
                    JSMN_ARRAY, -1, -1, 3,
                    JSMN_PRIMITIVE, "1",
                    JSMN_PRIMITIVE, "true",
                    JSMN_ARRAY, -1, -1, 2,
                    JSMN_PRIMITIVE, "123",
                    JSMN_STRING, "hello", 0));
    }
    return 0;
}

int test_unquoted_keys(void) {
#ifndef JSMN_STRICT
    int r;
    jsmn_parser p;
    jsmntok_t tok[10];
    const char *js;

    jsmn_init(&p);
    js = "key1: \"value\"\nkey2 : 123";

    r = jsmn_parse(&p, js, strlen(js), tok, 10);
    check(r >= 0);
    check(tokeq(js, tok, 4,
                JSMN_PRIMITIVE, "key1",
                JSMN_STRING, "value", 0,
                JSMN_PRIMITIVE, "key2",
                JSMN_PRIMITIVE, "123"));
#endif
    return 0;
}

int test_issue_22(void) {
    int r;
    jsmn_parser p;
    jsmntok_t tokens[128];
    const char *js;

    js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, "
         "\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", "
         "\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], "
         "\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, "
         "\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", "
         "\"imageheight\":64, \"imagewidth\":160, \"margin\":0, \"name\":\"Tiles\", "
         "\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 }], "
         "\"tilewidth\":32, \"version\":1, \"width\":10 }";
    jsmn_init(&p);
    r = jsmn_parse(&p, js, strlen(js), tokens, 128);
    check(r >= 0);
    return 0;
}

int test_issue_27(void) {
    const char *js =
        "{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", ";
    check(parse(js, JSMN_ERROR_PART, 8));
    return 0;
}

int test_input_length(void) {
    const char *js;
    int r;
    jsmn_parser p;
    jsmntok_t tokens[10];

    js = "{\"a\": 0}garbage";

    jsmn_init(&p);
    r = jsmn_parse(&p, js, 8, tokens, 10);
    check(r == 3);
    check(tokeq(js, tokens, 3,
                JSMN_OBJECT, -1, -1, 1,
                JSMN_STRING, "a", 1,
                JSMN_PRIMITIVE, "0"));
    return 0;
}

int test_count(void) {
    jsmn_parser p;
    const char *js;

    js = "{}";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);

    js = "[]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);

    js = "[[]]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2);

    js = "[[], []]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);

    js = "[[], []]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);

    js = "[[], [[]], [[], []]]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);

    js = "[\"a\", [[], []]]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);

    js = "[[], \"[], [[]]\", [[]]]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);

    js = "[1, 2, 3]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4);

    js = "[1, 2, [3, \"a\"], null]";
    jsmn_init(&p);
    check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);

    return 0;
}


int test_nonstrict(void) {
#ifndef JSMN_STRICT
    const char *js;
    js = "a: 0garbage";
    check(parse(js, 2, 2,
                JSMN_PRIMITIVE, "a",
                JSMN_PRIMITIVE, "0garbage"));

    js = "Day : 26\nMonth : Sep\n\nYear: 12";
    check(parse(js, 6, 6,
                JSMN_PRIMITIVE, "Day",
                JSMN_PRIMITIVE, "26",
                JSMN_PRIMITIVE, "Month",
                JSMN_PRIMITIVE, "Sep",
                JSMN_PRIMITIVE, "Year",
                JSMN_PRIMITIVE, "12"));
#endif
    return 0;
}

int main(void) {
    test(test_empty, "test for a empty JSON objects/arrays");
    test(test_object, "test for a JSON objects");
    test(test_array, "test for a JSON arrays");
    test(test_primitive, "test primitive JSON data types");
    test(test_string, "test string JSON data types");

    test(test_partial_string, "test partial JSON string parsing");
    test(test_partial_array, "test partial array reading");
    test(test_array_nomem, "test array reading with a smaller number of tokens");
    test(test_unquoted_keys, "test unquoted keys (like in JavaScript)");
    test(test_input_length, "test strings that are not null-terminated");
    test(test_issue_22, "test issue #22");
    test(test_issue_27, "test issue #27");
    test(test_count, "test tokens count estimation");
    test(test_nonstrict, "test for non-strict mode");
    printf("\nPASSED: %d FAILED: %d\n", test_passed, test_failed); // CHECK: PASSED: 14 FAILED: 0

    return (test_failed > 0);
}

